﻿using System.Linq.PageList;
using Dyao.Repository.Abstractions;
using Dyao.Repository.Abstractionsx;
using Dyao.Book.Application.Abstractions;
using Dyao.Book.BusApplication;
using Dyao.Book.Contract;
using Dyao.Book.Core;
using Dyao.Book.Entity;
using Foundation.Core;
using Foundation.DistributeLock;
using Foundation.Uow;
using Microsoft.Extensions.DependencyInjection;

namespace Dyao.Book.Application
{
    /// <summary>
    /// 任务中心记录
    /// </summary>
    public class TaskCenterRecordApplication : BaseApplication, ITaskCenterRecordApplication
    {
        private readonly ITaskCenterRecordRepository _taskCenterRecordRepository;
        private readonly IDistributeLocker _distributeLocker;
        private readonly ITaskCenterSettingsRepository _taskCenterSettingsRepository;
        private readonly ITaskCenterRecordLogRepository _taskCenterRecordLogRepository;
        private readonly ITaskCenterRecordExtensionRepository _taskCenterRecordExtensionReposity;
        private readonly ITaskCenterRecordViewRepository _taskCenterRecordViewRepository;

        public TaskCenterRecordApplication(ITaskCenterRecordRepository taskCenterRecordRepository
            , IDistributeLocker distributeLocker
            , ITaskCenterSettingsRepository taskCenterSettingsRepository
            , ITaskCenterRecordLogRepository TaskCenterRecordLogRepository
            , ITaskCenterRecordExtensionRepository taskCenterRecordExtensionReposity
            , ITaskCenterRecordViewRepository taskCenterRecordViewRepository)
        {
            _taskCenterRecordRepository = taskCenterRecordRepository;
            this._distributeLocker = distributeLocker;
            this._taskCenterSettingsRepository = taskCenterSettingsRepository;
            this._taskCenterRecordLogRepository = TaskCenterRecordLogRepository;
            this._taskCenterRecordExtensionReposity = taskCenterRecordExtensionReposity;
            this._taskCenterRecordViewRepository = taskCenterRecordViewRepository;
        }

        /// <summary>
        /// 获取任务记录
        /// </summary>
        /// <param name="id">id</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task<TaskCenterRecordInfoDto> GetCenterRecordInfoModelAsync(int id, CancellationToken cancellationToken = default)
        {
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var taskCenterRecordInfoModel = await _taskCenterRecordExtensionReposity.GetByIdAsync(id, cancellationToken);
                return taskCenterRecordInfoModel;
            }
        }

        /// <summary>
        /// 是否有当前任务正在运行
        /// </summary>
        /// <param name="settingId">taskId</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task<bool> IsHaveCurrentTaskRunningAsync(string settingId, CancellationToken cancellationToken = default)
        {
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var isHaveCurrentTaskRunning = await _taskCenterRecordRepository.IsExistAsync(m =>
                                                            (m.Status == TaskCenterRecordStatusEnum.Pending
                                                            || m.Status == TaskCenterRecordStatusEnum.Excuting)
                                                            && m.TaskCenterSettingsId == settingId, cancellationToken);
                return isHaveCurrentTaskRunning;
            }
        }

        /// <summary>
        /// 当前用户是否有正在运行的任务
        /// </summary>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task<bool> IsCurrentUserHaveRunningTaskAsync(CancellationToken cancellationToken = default)
        {
            var currentUserId = Convert.ToInt32(CurrentUser.Id);
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var isUserHaveRunningTask = await _taskCenterRecordRepository.IsExistAsync(m => (m.CreateUserId == currentUserId
                                                                                        || m.UpdateUserId == currentUserId)
                                                                                      && (m.Status == TaskCenterRecordStatusEnum.Pending
                                                                                        || m.Status == TaskCenterRecordStatusEnum.Excuting), cancellationToken);
                return isUserHaveRunningTask;
            }
        }

        /// <summary>
        /// 获取任务记录
        /// </summary>
        /// <param name="pageFilter">pageFilter</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task<PageList<TaskCenterRecordPageDto>> GetTaskCenterRecordModelPagedListAsync(IPageFilter<TaskCenterRecordFilterDto, TaskCenterRecordOrderByDto> pageFilter
            , CancellationToken cancellationToken = default)
        {
            if (!string.IsNullOrWhiteSpace(pageFilter.Filter.SearchText))
            {
                var list = TaskCenterCodeManager.GetAllCodes()
                     .Where(a => a.TaskCode.Contains(pageFilter.Filter.SearchText))
                     .Select(a => a.TaskCode)
                     .ToList();

                if (!list.Any())
                {
                    list.Add(pageFilter.Filter.SearchText);
                }

                pageFilter.Filter.TaskSettingsIdList = list;
            }

            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var pageList = await _taskCenterRecordViewRepository.GetTaskCenterRecordModelPagedListAsync(pageFilter, cancellationToken);
                var modelList = ObjectMapper.Map<List<TaskCenterRecordKeylessEntity>, List<TaskCenterRecordPageDto>>(pageList.Data);
                var pageModelList = new PageList<TaskCenterRecordPageDto>(pageList.PageIndex, pageList.PageSize);
                pageModelList.SetResult(modelList, pageList.Records);

                return pageModelList;
            }
        }

        /// <summary>
        /// 删除任务记录
        /// </summary>
        /// <param name="taskCenterRecordDeleteModel">taskCenterRecordDeleteModel</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task DeleteAsync(TaskCenterRecordDeleteDto taskCenterRecordDeleteModel, CancellationToken cancellationToken = default)
        {
            var resource = TaskCenterHelper.GetUpdateTaskCenterRecordResource(taskCenterRecordDeleteModel.TaskCenterRecordId);
            var redLock = await _distributeLocker.CreateBlockingLockAsync(resource, TimeSpan.FromMinutes(10), TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(5), cancellationToken);
            await redLock.UsingBlockingLockAsync(async () =>
            {
                using (var unitOfWork = UnitOfWorkManager.Begin())
                {
                    var taskCenterRecord = await _taskCenterRecordRepository.GetAsync(taskCenterRecordDeleteModel.TaskCenterRecordId, cancellationToken);
                    if (taskCenterRecord.Status == TaskCenterRecordStatusEnum.Excuting)
                    {
                        throw new BusinessException(CommonExceptionCode.TaskCenterDeleteRecordValidateStatusExcuting, "执行中的任务记录不允许删除");
                    }

                    taskCenterRecord.IsDel = true;
                    taskCenterRecord.UpdateDateTime = DateTime.Now;

                    await unitOfWork.SaveChangesAsync(cancellationToken);
                }
            });
        }

        #region 验证用户任务

        /// <summary>
        /// 验证同一用户同一任务时间间隔
        /// </summary>
        /// <param name="taskSettingId">任务Id</param>
        /// <param name="intervalSeconds">间隔秒数</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task<bool> HaveRecordAsync(string taskSettingId, int intervalSeconds, CancellationToken cancellationToken = default)
        {
            var currentUserId = CurrentUser.GetUserId().ToInt();
            var intervalDateTime = DateTime.Now.AddSeconds(-intervalSeconds);
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var isHaveRecord = await _taskCenterRecordRepository.IsExistAsync(m => m.TaskCenterSettingsId == taskSettingId
                                                                                && m.CreateDateTime >= intervalDateTime
                                                                                && m.CreateUserId == currentUserId, cancellationToken);
                return isHaveRecord;
            }
        }

        #endregion

        /// <summary>
        /// 自动检查重试
        /// </summary>
        /// <param name="defaultTimeoutMinutes">默认超时分钟</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task AutoCheckAndRetryAsync(int defaultTimeoutMinutes, CancellationToken cancellationToken = default)
        {
            var pageSize = 500;
            await HandleTimeoutListAsync(defaultTimeoutMinutes, pageSize, cancellationToken);

            await HandleRetryListAsync(defaultTimeoutMinutes, pageSize, cancellationToken);
        }

        /// <summary>
        /// 检查超时任务
        /// </summary>
        /// <param name="taskCenterCheckRetryModel">taskCenterCheckRetryModel</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task UpdateTimeoutAsync(TaskCenterCheckRetryDto taskCenterCheckRetryModel, CancellationToken cancellationToken = default)
        {
            List<TaskCenterRecordLogDto> logList = new List<TaskCenterRecordLogDto>();
            var redLock = await GetNoBlockingRedLockAsync(taskCenterCheckRetryModel.RecordId);
            await redLock.UsingNonBlockingLockAsync(async () =>
            {
                using (var unitOfWork = UnitOfWorkManager.Begin(true))
                {
                    var taskCenterRecord = await _taskCenterRecordRepository.GetAsync(taskCenterCheckRetryModel.RecordId, cancellationToken);
                    if (!(taskCenterRecord.RetryCountMax == taskCenterRecord.RetryCount
                          && taskCenterRecord.Status == TaskCenterRecordStatusEnum.Excuting
                          && taskCenterRecord.ExcuteBeginDatetime.Value.AddSeconds(taskCenterRecord.TimeOutSecond ?? taskCenterCheckRetryModel.DefaultTimeoutMinutes * 60) < DateTime.Now))
                    {
                        return;
                    }

                    taskCenterRecord.Status = TaskCenterRecordStatusEnum.ExcuteFail;
                    taskCenterRecord.IsTimeOut = true;
                    taskCenterRecord.UpdateDateTime = DateTime.Now;

                    await unitOfWork.SaveChangesAsync(cancellationToken);

                    var message = "执行超时，状态设置为执行失败";
                    TaskCenterRecordLogDto log = GetRecordLogModel(taskCenterRecord, TaskCenterRecordLogTypeEnum.Business, message, null, null);
                    logList.Add(log);
                }

                using (var unitOfWork = UnitOfWorkManager.Begin(true, true))
                {
                    var insertLogList = ObjectMapper.Map<List<TaskCenterRecordLogDto>, List<TaskCenterRecordLogEntity>>(logList);
                    await _taskCenterRecordLogRepository.InsertRangeAsync(insertLogList, false, cancellationToken);
                    await unitOfWork.CompleteAsync(cancellationToken);
                }
            });
        }

        /// <summary>
        /// 重试超时任务
        /// </summary>
        /// <param name="taskCenterCheckRetryModel">taskCenterCheckRetryModel</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task RetryTimeoutAsync(TaskCenterCheckRetryDto taskCenterCheckRetryModel, CancellationToken cancellationToken = default)
        {
            List<TaskCenterRecordLogDto> logList = new List<TaskCenterRecordLogDto>();
            var redLock = await GetNoBlockingRedLockAsync(taskCenterCheckRetryModel.RecordId);
            await redLock.UsingNonBlockingLockAsync(async () =>
            {
                using (var unitOfWork = UnitOfWorkManager.Begin(true, true))
                {
                    var taskCenterRecord = await _taskCenterRecordRepository.GetAsync(taskCenterCheckRetryModel.RecordId, cancellationToken);
                    if (!(taskCenterRecord.RetryCountMax > taskCenterRecord.RetryCount
                          && taskCenterRecord.Status == TaskCenterRecordStatusEnum.ExcuteFail
                          || (taskCenterRecord.Status == TaskCenterRecordStatusEnum.Excuting
                              && taskCenterRecord.ExcuteBeginDatetime.Value.AddSeconds(taskCenterRecord.TimeOutSecond ?? taskCenterCheckRetryModel.DefaultTimeoutMinutes * 60) < DateTime.Now)))
                    {
                        return;
                    }

                    //超时更新状态
                    if (taskCenterRecord.Status == TaskCenterRecordStatusEnum.Excuting)
                    {
                        taskCenterRecord.Status = TaskCenterRecordStatusEnum.ExcuteFail;
                        taskCenterRecord.IsTimeOut = true;

                        var message = "执行超时，状态设置为执行失败";
                        TaskCenterRecordLogDto log = GetRecordLogModel(taskCenterRecord, TaskCenterRecordLogTypeEnum.Business, message, null, null);
                        logList.Add(log);
                    }

                    //重试更新状态
                    taskCenterRecord.Status = TaskCenterRecordStatusEnum.Pending;
                    taskCenterRecord.RetryCount = taskCenterRecord.RetryCount + 1;
                    taskCenterRecord.UpdateDateTime = DateTime.Now;
                    TaskCenterRecordLogDto TaskCenterRecordLogDto = GetRecordLogModel(taskCenterRecord, TaskCenterRecordLogTypeEnum.Business, $"正在第{taskCenterRecord.RetryCount}次重试", null, null);
                    logList.Add(TaskCenterRecordLogDto);

                    //发送Command
                    var settingModel = await _taskCenterSettingsRepository.GetAsync(taskCenterRecord.TaskCenterSettingsId, cancellationToken);
                    var taskCenterRecordExtension = await _taskCenterRecordExtensionReposity.GetAsync(taskCenterCheckRetryModel.RecordId, cancellationToken);
                    var command = CreateTaskCenterCommand(taskCenterRecord, settingModel);

                    unitOfWork.OnCompleted(() => { ServiceBus.SendOneWayCommand(command); return Task.CompletedTask; }); //提交事务成功后再发送命令

                    await unitOfWork.SaveChangesAsync(cancellationToken);
                    await unitOfWork.CompleteAsync(cancellationToken);
                }

                using (var unitOfWork = UnitOfWorkManager.Begin(true, true))
                {
                    var insertLogList = ObjectMapper.Map<List<TaskCenterRecordLogDto>, List<TaskCenterRecordLogEntity>>(logList);
                    await _taskCenterRecordLogRepository.InsertRangeAsync(insertLogList, false, cancellationToken);
                    await unitOfWork.CompleteAsync(cancellationToken);
                }
            });
        }

        /// <summary>
        /// 获取扩展表执行信息内容
        /// </summary>
        /// <param name="recordId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<string> GetRecordExtensionExcutingInfoAsync(int recordId, CancellationToken cancellationToken = default)
        {
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var excutingInfoEntity = await _taskCenterRecordExtensionReposity.FindAsync(m => m.ID == recordId, cancellationToken: cancellationToken);
                return excutingInfoEntity?.ExcutingInfo;
            }
        }

        /// <summary>
        /// 获取扩展表结果信息内容
        /// </summary>
        /// <param name="recordId"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<string> GetRecordExtensionResultAsync(int recordId, CancellationToken cancellationToken = default)
        {
            using (var unitOfWork = UnitOfWorkManager.Begin())
            {
                var info = await _taskCenterRecordExtensionReposity.GetByPropertyNameAsync(recordId, new string[] { nameof(TaskCenterRecordExtensionEntity.ExcuteResult) }, cancellationToken: cancellationToken);
                return info?.ExcuteResult;
            }
        }

        #region 清理之前记录

        /// <summary>
        /// 删除已完成的任务记录
        /// </summary>
        /// <param name="deleteRecordTypeEnum">删除记录类型</param>
        /// <param name="preHour">preHour</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        public async Task DeleteCompleteAsync(DeleteRecordTypeEnum deleteRecordTypeEnum, int preHour, CancellationToken cancellationToken = default)
        {
            var pageSize = 500;
            List<int> recordIdList = null;
            var preCreateDateTime = DateTime.Now.AddHours(preHour);
            while (true)
            {
                using (var unitOfWork = UnitOfWorkManager.Begin(true))
                {
                    recordIdList = await _taskCenterRecordRepository.GetCompleteTaskRecordIdListForDeleteAsync(deleteRecordTypeEnum, preCreateDateTime, pageSize, cancellationToken);
                    if (recordIdList.Any())
                    {
                        await _taskCenterRecordRepository.DeleteAsync(m => recordIdList.Contains(m.ID), true, cancellationToken);
                    }
                    else
                    {
                        return;
                    }
                }

                using (var unitOfWork = UnitOfWorkManager.Begin(true))
                {
                    await _taskCenterRecordLogRepository.DeleteAsync(m => recordIdList.Contains(m.TaskCenterRecordId), true, cancellationToken);
                }
            }
        }

        #endregion

        /// <summary>
        /// 创建执行的Command
        /// </summary>
        /// <param name="taskCenterRecord">taskCenterRecord</param>
        /// <param name="settingModel">settingModel</param>
        /// <param name="searchParams">searchParams</param>
        /// <returns></returns>
        private ITaskCenterCommand CreateTaskCenterCommand(TaskCenterRecordEntity taskCenterRecord, TaskCenterSettingsEntity settingModel)
        {
            ITypeFinder typeFinder = ServiceProvider.GetService<ITypeFinder>();
            Type type = typeFinder.GetByFullName(settingModel.CommandName);

            var command = (ITaskCenterCommand)Activator.CreateInstance(type);

            command.TaskCenterDto = new TaskCenterDto
            {
                TaskCenterRecordId = taskCenterRecord.ID,
                CurrentRetryCount = taskCenterRecord.RetryCount,
                Category = taskCenterRecord.Category.ToString(),
                TaskCode = taskCenterRecord.TaskCenterSettingsId
            };

            return command;
        }

        /// <summary>
        /// 获取记录日志
        /// </summary>
        /// <param name="taskCenterRecord">taskCenterRecord</param>
        /// <param name="typeEnum">typeEnum</param>
        /// <param name="message">message</param>
        /// <param name="remark">remark</param>
        /// <param name="content">content</param>
        /// <returns></returns>
        private static TaskCenterRecordLogDto GetRecordLogModel(TaskCenterRecordEntity taskCenterRecord, TaskCenterRecordLogTypeEnum typeEnum, string message, string remark, string content)
        {
            return new TaskCenterRecordLogDto
            {
                TaskCenterRecordId = taskCenterRecord.ID,
                Content = content,
                Remark = remark,
                Message = message,
                Type = typeEnum,
                CreateUserName = taskCenterRecord.CreateUserName,
                CreateDateTime = DateTime.Now
            };
        }

        /// <summary>
        /// 获取锁
        /// </summary>
        /// <param name="taskCenterRecordId">taskCenterRecordId</param>
        /// <returns></returns>
        private async Task<ILock> GetNoBlockingRedLockAsync(int taskCenterRecordId)
        {
            var resource = TaskCenterHelper.GetUpdateTaskCenterRecordResource(taskCenterRecordId);
            var redLock = await _distributeLocker.CreateNonBlockingLockAsync(resource, TimeSpan.FromSeconds(1));
            return redLock;
        }

        /// <summary>
        /// 修改当前用户
        /// </summary>
        /// <param name="taskCenterRecord"></param>
        private IDisposable ChangeUser(TaskCenterRecordEntity taskCenterRecord)
        {
            var user = ServiceProvider.GetService<ICurrentUserAccessor>();
            return user.Change(new CurrentUserModel
            {
                UserId = taskCenterRecord.CreateUserId,
                UserName = taskCenterRecord.CreateUserName
            });
        }

        /// <summary>
        /// 处理超时列表
        /// </summary>
        /// <param name="defaultTimeoutMinutes">defaultTimeoutMinutes</param>
        /// <param name="pageSize">pageSize</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        private async Task HandleTimeoutListAsync(int defaultTimeoutMinutes, int pageSize, CancellationToken cancellationToken)
        {
            var retryFlag = true;
            var pageIndex = 0;
            while (retryFlag)
            {
                using (var unitOfWork = UnitOfWorkManager.Begin())
                {
                    //获取重试列表
                    var retryTaskCenterRecordList = await _taskCenterRecordRepository.GetNeedRetryListAsync(pageIndex, pageSize, cancellationToken);
                    if (retryTaskCenterRecordList.Any())
                    {
                        foreach (var taskCenterRecord in retryTaskCenterRecordList)
                        {
                            if (!(taskCenterRecord.Status == TaskCenterRecordStatusEnum.ExcuteFail
                                  || (taskCenterRecord.Status == TaskCenterRecordStatusEnum.Excuting
                                      && taskCenterRecord.ExcuteBeginDatetime.Value.AddSeconds(taskCenterRecord.TimeOutSecond ?? defaultTimeoutMinutes * 60) < DateTime.Now)))
                            {
                                continue;
                            }

                            using (ChangeUser(taskCenterRecord))
                            {
                                var command = new RetryTaskCenterRecordCommand
                                {
                                    Data = new TaskCenterCheckRetryDto
                                    {
                                        RecordId = taskCenterRecord.ID,
                                        DefaultTimeoutMinutes = defaultTimeoutMinutes
                                    }
                                };
                                ServiceBus.SendOneWayCommand(command);
                            }
                        }
                        pageIndex++;
                    }
                    else
                    {
                        retryFlag = false;
                    }

                }

            }
        }

        /// <summary>
        /// 处理重试列表
        /// </summary>
        /// <param name="defaultTimeoutMinutes">defaultTimeoutMinutes</param>
        /// <param name="pageSize">pageSize</param>
        /// <param name="cancellationToken">cancellationToken</param>
        /// <returns></returns>
        private async Task HandleRetryListAsync(int defaultTimeoutMinutes, int pageSize, CancellationToken cancellationToken)
        {
            var checkFlag = true;
            var pageIndex1 = 0;
            while (checkFlag)
            {
                using (var unitOfWork = UnitOfWorkManager.Begin())
                {
                    //获取超时列表
                    var taskCenterRecordTimeoutList = await _taskCenterRecordRepository.GetTimeoutListAsync(pageIndex1, pageSize, cancellationToken);
                    if (taskCenterRecordTimeoutList.Any())
                    {
                        foreach (var taskCenterRecord in taskCenterRecordTimeoutList)
                        {
                            if (!(taskCenterRecord.ExcuteBeginDatetime.Value.AddSeconds(taskCenterRecord.TimeOutSecond ?? defaultTimeoutMinutes * 60) < DateTime.Now))
                            {
                                continue;
                            }

                            using (ChangeUser(taskCenterRecord))
                            {
                                var command = new UpdateTaskCenterRecordTimeoutCommand
                                {
                                    Data = new TaskCenterCheckRetryDto
                                    {
                                        RecordId = taskCenterRecord.ID,
                                        DefaultTimeoutMinutes = defaultTimeoutMinutes
                                    }
                                };
                                ServiceBus.SendOneWayCommand(command);
                            }
                        }
                        pageIndex1++;
                    }
                    else
                    {
                        checkFlag = false;
                    }
                }
            }
        }

    }
}
